package com.libre.workflow.service.impl;

import com.libre.common.exception.BusinessRuntimeException;
import com.libre.config.flowable.style.MyProcessDiagramGenerator;
import com.libre.workflow.service.MyResourceService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ExecutionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.ui.common.service.exception.BadRequestException;
import org.flowable.ui.common.service.exception.BaseModelerRestException;
import org.flowable.ui.common.service.exception.InternalServerErrorException;
import org.flowable.ui.modeler.domain.Model;
import org.flowable.ui.modeler.model.ModelRepresentation;
import org.flowable.ui.modeler.repository.ModelRepository;
import org.flowable.ui.modeler.service.FlowableModelQueryService;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipInputStream;

/**
 * @description 模型管理 创建/删除/发布
 * @author 庞留杰
 * @date 2020/9/11 18:01
 * @version V1.0
 **/
@Slf4j
@Service
@Transactional
public class MyResourceServiceImpl implements MyResourceService {

    @Autowired
    private ProcessEngine processEngine;
    @Autowired
    private ModelService modelService;
    @Autowired
    ModelRepository modelRepository;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    protected FlowableModelQueryService modelQueryService;

    /**
     * 功能描述: 查看流程图片/XML【查看图片：只是一个图片，不会显示执行到那个节点】
     *          访问示例： http://localhost:8888/model/resource?procDefId=askForLeave:1:8b0c7d46-f30c-11ea-a46e-405bd8b55521&proInsId=c6dc39f7-f30c-11ea-a46e-405bd8b55521&resType=image
     * @author: 庞留杰
     * @param processDefinitionId:流程定义ID
     * @param processInstanceId:流程实例ID
     * @param resourceType:资源类型(xml|image)
     * @return:
     * @exception
     * @date: 2020/9/11 19:04
     */
    @Override
    public void resourceRead(String processDefinitionId, String processInstanceId, String resourceType, HttpServletResponse response) throws Exception {
        if (StringUtils.isBlank(processDefinitionId)) {
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            processDefinitionId = processInstance.getProcessDefinitionId();
        }
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        String resourceName = "";
        if (resourceType.equals("image")) {
            resourceName = processDefinition.getDiagramResourceName();
        } else if (resourceType.equals("xml")) {
            resourceName = processDefinition.getResourceName();
        }
        InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
        byte[] b = new byte[1024];
        int len = -1;
        while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
            response.getOutputStream().write(b, 0, len);
        }
    }

    /**
     * 功能描述: 生成当前流程图表【会显示执行到那个节点，但是一旦流程结束，图片便会失效，只可以用作执行过程中的流程图展示】
     * @author: 庞留杰
     * @param processInstanceId:流程实例Id
     * @return:
     * @exception
     * @date: 2020/9/11 19:21
     */
//    @Override
//    public void getProcessDiagram(String processInstanceId, HttpServletResponse response) throws Exception {
//        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
//        //流程走完的不显示图
//        if (pi == null) {
//            return;
//        }
//        /* 在并行网关中不要调用singleResult()方法，会造成错误，可以改用list()方法，2021年1月21日15:17:37
//        Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
//        //使用流程实例ID，查询正在执行的执行对象表，返回流程实例对象
//        String InstanceId = task.getProcessInstanceId();*/
//        List<Execution> executions = runtimeService.createExecutionQuery()
//                .processInstanceId(processInstanceId).list();
//        //得到正在执行的Activity的Id
//        List<String> activityIds = new ArrayList();
//        List<String> flows = new ArrayList();
//        for (Execution exe : executions) {
//            List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
//            activityIds.addAll(ids);
//        }
//
//         /**
//            可以让所有走过的节点和线高亮显示
//            //获得已经办理的历史节点
//            List<HistoricActivityInstance> activityInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
//            for (HistoricActivityInstance activityInstance : activityInstances) {
//                if ("sequenceFlow".equals(activityInstance.getActivityType())) {
//                    //需要高亮显示的连接线
//                    flows.add(activityInstance.getActivityId());
//                } else {
//                    //需要高亮显示的节点
//                     activityIds.add(activityInstance.getActivityId());
//                }
//            }
//        */
//
//        //获取流程图
//        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
//        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
//        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
//        /**
//         * 防止图片乱码
//         *   ActivityFontName：默认获取的是 “Arial”体
//         *   LabelFontName：默认获取的是 “Arial”体
//         *   AnnotationFontName：默认获取的是 “Arial”体
//         * */
//        //InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds,
//        //        flows,engconf.getActivityFontName(),engconf.getLabelFontName(),engconf.getAnnotationFontName(),
//        //        engconf.getClassLoader(),1.0,true);
//        //此处写死"宋体"，可以防止页面展示乱码
//        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds,
//                flows, "宋体", "宋体", "宋体",
//                engconf.getClassLoader(), 1.0, true);
//        OutputStream out = null;
//        byte[] buf = new byte[1024];
//        int legth = 0;
//        try {
//            out = response.getOutputStream();
//            while ((legth = in.read(buf)) != -1) {
//                out.write(buf, 0, legth);
//            }
//        } finally {
//            if (in != null) {
//                in.close();
//            }
//            if (out != null) {
//                out.close();
//            }
//        }
//    }





    /**
     * 功能描述: 获取流程图（包含 正在进行中+已结束 的流程图）
     */
    @Override
    public void flowDiagram(String processInstanceId, String executionId, HttpServletResponse response) {
        /**
         * 获得当前活动的节点
         */
        String processDefinitionId = "";
        /**
         * 获得活动的节点
         */
        List<String> highLightedActivitis = new ArrayList<>();
        if (this.isFinished(processInstanceId)) {// 如果流程已经结束，则得到结束节点
            HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            processDefinitionId = pi.getProcessDefinitionId();

            // 结束节点高亮显示
            List<HistoricActivityInstance> activityInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
            for (HistoricActivityInstance activityInstance : activityInstances) {
                String activityId = activityInstance.getActivityId();
                if ("endEvent".equals(activityInstance.getActivityType())) {//结束节点
                    highLightedActivitis.add(activityInstance.getActivityId());  //需要高亮显示的节点
                }
            }

        } else {// 如果流程没有结束，则取当前活动节点
            // 根据流程实例ID获得当前处于活动状态的ActivityId合集
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            processDefinitionId=pi.getProcessDefinitionId();
            ExecutionQuery executionQuery = runtimeService.createExecutionQuery();
            executionQuery.processInstanceId(processInstanceId);
            //用于多实例
            if (StringUtils.isNotEmpty(executionId)) { //执行实例ID
                executionQuery.executionId(executionId);
            }
            List<Execution> executions = executionQuery.list();
            for (Execution exe : executions) {
                List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
                highLightedActivitis.addAll(ids);
            }
        }
        List<String> flows = new ArrayList<>();

        //让所有走过的节点高亮
//        List<HistoricActivityInstance> activityInstances =  historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
//        for(HistoricActivityInstance activityInstance : activityInstances){
//            String activityId = activityInstance.getActivityId();
//            // highLightedActivitis.add(activityId);//所有高亮
//            String[] gateway = {"exclusiveGateway","parallelGateway","inclusiveGateway","eventGateway"};
//            if(!Arrays.asList(gateway).contains(activityInstance.getActivityType())){//网关不高亮显示
//                if ("sequenceFlow".equals(activityInstance.getActivityType())) {
//                    //需要高亮显示的连接线
//                    flows.add(activityInstance.getActivityId());
//                } else {
//                    //需要高亮显示的节点
//                    highLightedActivitis.add(activityInstance.getActivityId());
//                }
//            }
//        }

        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();

        // ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
        ProcessDiagramGenerator diagramGenerator = new MyProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "bmp", highLightedActivitis, flows,
                engconf.getActivityFontName(),
                engconf.getLabelFontName(),
                engconf.getAnnotationFontName(),
                engconf.getClassLoader(), 1.0, true);


        OutputStream out = null;
        byte[] buf = new byte[1024];
        int legth = 0;
        try {
            out = response.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        } catch (IOException e) {
            log.error("操作异常",e);
        } finally {
            /*IOUtils.closeQuietly(out);
            IOUtils.closeQuietly(in);*/
            try {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // 判断流程是否结束
    public boolean isFinished(String processInstanceId) {
        return historyService.createHistoricProcessInstanceQuery().finished()
                .processInstanceId(processInstanceId).count() > 0;
    }

    /**
     * 功能描述: 下载xml
     *       摘抄源码：org.flowable.ui.modeler.rest.app -> /rest/models/{processModelId}/bpmn20
     * @author: 庞留杰
     * @param processDefinitionId:流程定义ID
     * @return:
     * @exception
     * @date: 2020/9/11 17:57
     */
    @Override
    public void downloadXml(String processDefinitionId, HttpServletResponse response) {
        String processModelId = processDefinitionId;
        if (processModelId == null) {
            throw new BadRequestException("No process model id provided");
        }
        Model model = modelService.getModel(processModelId);
        String name = model.getName().replaceAll(" ", "_") + ".bpmn20.xml";
        String encodedName = null;
        try {
            encodedName = "UTF-8''" + URLEncoder.encode(name, "UTF-8");
        } catch (Exception e) {
            log.warn("编码名称失败 " + name);
        }
        String contentDispositionValue = "attachment; filename=" + name;
        if (encodedName != null) {
            contentDispositionValue += "; filename*=" + encodedName;
        }
        response.setHeader("Content-Disposition", contentDispositionValue);
        if (model.getModelEditorJson() != null) {
            try {
                ServletOutputStream servletOutputStream = response.getOutputStream();
                response.setContentType("application/xml");
                BpmnModel bpmnModel = modelService.getBpmnModel(model);
                byte[] xmlBytes = modelService.getBpmnXML(bpmnModel);
                BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(xmlBytes));
                byte[] buffer = new byte[8096];
                while (true) {
                    int count = in.read(buffer);
                    if (count == -1) {
                        break;
                    }
                    servletOutputStream.write(buffer, 0, count);
                }
                // 刷新和关闭流
                servletOutputStream.flush();
                servletOutputStream.close();
            } catch (BaseModelerRestException e) {
                throw e;
            } catch (Exception e) {
                log.error("Could not generate BPMN 2.0 XML", e);
                throw new InternalServerErrorException("Could not generate BPMN 2.0 xml");
            }
        }
    }

    @Override
    public Deployment deployByZip(String name, String category, String tenantId, MultipartFile file) {
        Deployment deployment = null;
        try (ZipInputStream zipIn = new ZipInputStream(file.getInputStream(), Charset.forName("UTF-8"))) {
            deployment = repositoryService.createDeployment().addZipInputStream(zipIn)
                    .name(name).category(category).tenantId(tenantId).deploy();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return deployment;
    }

    /**
     * 功能描述: 利用此方式,可以将文件导入到"act_de_model"中
     *           摘抄源码: org.flowable.ui.modeler.rest -> POST "app/rest/import-case-model"
     * @author: 庞留杰
     * @param file
     * @return: Result
     * @exception
     * @date: 2020/9/21 10:48
     */
    @Override
    public ModelRepresentation importModel(MultipartFile file, HttpServletRequest request) {
        String fileName = file.getOriginalFilename();
        if (fileName != null && !fileName.endsWith(".bpmn") && !fileName.endsWith(".bpmn20.xml")) {
            throw new BusinessRuntimeException("文件类型不正确，只能上传.bpmn/.bpmn20.xml文件");
        }
        ModelRepresentation modelRepresentation = modelQueryService.importProcessModel(request, file);
        return modelRepresentation;
    }

}
